﻿using KeraLua;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using LuaState = KeraLua.Lua;
namespace QLua.Extensions;

static class LuaExtensions
{
	public static bool CheckMetaTable(this LuaState state, int index, IntPtr tag)
	{
		if (!state.GetMetaTable(index))
			return false;

		state.PushLightUserData(tag);
		state.RawGet(-2);
		bool isNotNil = !state.IsNil(-1);
		state.SetTop(-3);
		return isNotNil;
	}

	public static void PopGlobalTable(this LuaState luaState)
	{
		luaState.RawSetInteger(LuaRegistry.Index, (long)LuaRegistryIndex.Globals);
	}

	public static void GetRef(this LuaState luaState, int reference)
	{
		luaState.RawGetInteger(LuaRegistry.Index, reference);
	}

	// ReSharper disable once IdentifierTypo
	public static void Unref(this LuaState luaState, int reference)
	{
		luaState.Unref(LuaRegistry.Index, reference);
	}

	public static bool AreEqual(this LuaState luaState, int ref1, int ref2)
	{
		return luaState.Compare(ref1, ref2, LuaCompare.Equal);
	}

	public static IntPtr CheckUData(this LuaState state, int ud, string name)
	{
		IntPtr p = state.ToUserData(ud);
		if (p == IntPtr.Zero)
			return IntPtr.Zero;
		if (!state.GetMetaTable(ud))
			return IntPtr.Zero;

		state.GetField(LuaRegistry.Index, name);

		bool isEqual = state.RawEqual(-1, -2);

		state.Pop(2);

		if (isEqual)
			return p;

		return IntPtr.Zero;
	}

	public static int ToNetObject(this LuaState state, int index, IntPtr tag)
	{
		if (state.Type(index) != LuaType.UserData)
			return -1;

		IntPtr userData;

		if (state.CheckMetaTable(index, tag))
		{
			userData = state.ToUserData(index);
			if (userData != IntPtr.Zero)
				return Marshal.ReadInt32(userData);
		}

		userData = state.CheckUData(index, "luaNet_class");
		if (userData != IntPtr.Zero)
			return Marshal.ReadInt32(userData);

		userData = state.CheckUData(index, "luaNet_searchbase");
		if (userData != IntPtr.Zero)
			return Marshal.ReadInt32(userData);

		userData = state.CheckUData(index, "luaNet_function");
		if (userData != IntPtr.Zero)
			return Marshal.ReadInt32(userData);

		return -1;
	}

	public static void NewUData(this LuaState state, int val)
	{
		IntPtr pointer = state.NewUserData(Marshal.SizeOf(typeof(int)));
		Marshal.WriteInt32(pointer, val);
	}

	public static int RawNetObj(this LuaState state, int index)
	{
		IntPtr pointer = state.ToUserData(index);
		if (pointer == IntPtr.Zero)
			return -1;

		return Marshal.ReadInt32(pointer);
	}

	public static int CheckUObject(this LuaState state, int index, string name)
	{
		IntPtr udata = state.CheckUData(index, name);
		if (udata == IntPtr.Zero)
			return -1;

		return Marshal.ReadInt32(udata);
	}

	public static bool IsNumericType(this LuaState state, int index)
	{
		return state.Type(index) == LuaType.Number;
	}
}
